using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using SBPweb.Data;

[assembly: TagPrefix("SBPweb.Controls.Web", "SBPweb")]

namespace SBPweb.Controls.Web
{
	[DefaultProperty("DataSource")]
	[ToolboxData("<{0}:EntityGridView runat=server></{0}:EntityGridView>")]
	public class EntityGridView: GridView
	{

		#region Private fields

		private string mTitle;

		#endregion

		#region Constructor logic

		public EntityGridView()
		{
			AllowPaging = true;
			AllowSorting = true;
			AutoGenerateColumns = true;
			AutoGenerateEditButton = true;
			AutoGenerateDeleteButton = true;

			CaptionAlign = TableCaptionAlign.Left;

			PageSize = 10;

			CssClass = "tableGrid";
			EditRowStyle.CssClass = "trGridEditRow";
			EmptyDataRowStyle.CssClass = "trGridEmptyRow";
			FooterStyle.CssClass = "trGridFooterRow";
			HeaderStyle.CssClass = "trGridHeaderRow";
			PagerStyle.CssClass = "trGridPagerRow";
			RowStyle.CssClass = "trGridRow";
			SelectedRowStyle.CssClass = "trGridSelectedRow";

		}

		#endregion

		#region Public property accessors

		public string Title
		{
			get
			{
				return mTitle;
			}
			set
			{
				mTitle = value;
			}
		}

		public string LastExpression
		{
			get
			{
				object ret = Page.Session[this.UniqueID + "_sortExpression"];
				if (ret != null)
				{
					return ret.ToString();
				}
				return null;
			}
			set
			{
				Page.Session[this.UniqueID + "_sortExpression"] = value;
			}
		}

		public SortDirection LastDirection
		{
			get
			{
				string key = this.UniqueID + "_sortDirection";
				SortDirection? sd = null;
				try
				{
					sd = (SortDirection)(Page.Session[key]);
				}
				catch
				{
					Page.Session.Add(key, sd = SortDirection.Ascending);
				}
				return sd.Value;
			}
			set
			{
				string key = this.UniqueID + "_sortDirection";
				Page.Session[key] = value;
			}
		}

		public new IEntityListSource DataSource
		{
			get
			{
				return base.DataSource as IEntityListSource;
			}
			set
			{
				base.DataSource = value;
			}
		}

		public new Entity SelectedValue
		{
			get
			{
				IEntityListSource source = DataSource;
				if(source == null || SelectedIndex < 0)
				{
					return null;
				}

				return source.AsList[SelectedIndex] as Entity;
			}
		}

		#endregion

		#region Public methods

		public void KeepSorting()
		{
			if (!string.IsNullOrEmpty(LastExpression))
			{
				if (LastDirection == SortDirection.Ascending)
				{
					LastDirection = SortDirection.Descending;
				}
				else
				{
					LastDirection = SortDirection.Ascending;
				}
				this.Sort(LastExpression, LastDirection);
			}
			else
			{
				DataBind();
			}
		}

		public TextBox GetSelectedCellTextBox(int columnIndex)
		{
			TableCell cell = SelectedRow.Cells[columnIndex];
			if (cell.Controls.Count > 0)
			{
				foreach (Control c in cell.Controls)
				{
					if (c is TextBox)
					{
						return c as TextBox;
					}
				}
			}
			return null;
		}

		public CheckBox GetSelectedCellCheckBox(int columnIndex)
		{
			TableCell cell = SelectedRow.Cells[columnIndex];
			if (cell.Controls.Count > 0)
			{
				foreach (Control c in cell.Controls)
				{
					if (c is CheckBox)
					{
						return c as CheckBox;
					}
				}
			}
			return null;
		}

		#endregion

		#region Public overridden events

		public new event EntityGridViewCommandEventHandler RowCommand;
		public new event EntityGridViewDeleteEventHandler RowDeleting;
		public new event EntityGridViewSelectEventHandler SelectedIndexChanging;
        public new event EntityGridViewUpdateEventHandler RowUpdating; 
        public new event EntityGridViewUpdatedEventHandler RowUpdated;

		#endregion

		#region Overridden handlers

		protected override void OnSorted(EventArgs e)
		{
 			 base.OnSorted(e);
			 DataBind();
		}

		protected override void OnSorting(GridViewSortEventArgs e)
		{
			if (LastDirection == SortDirection.Ascending)
			{
				LastDirection = SortDirection.Descending;
			}
			else
			{
				LastDirection = SortDirection.Ascending;
			}
			LastExpression = e.SortExpression;
			DataSource.Sort(e.SortExpression, LastDirection);
		}

		protected override void OnSelectedIndexChanged(EventArgs e)
		{
			base.OnSelectedIndexChanged(e);
		}

		protected override void OnSelectedIndexChanging(GridViewSelectEventArgs e)
		{
			base.OnSelectedIndexChanging(e);
			if(SelectedIndexChanging != null)
			{
				EntityGridViewSelectEventArgs gvsea = new EntityGridViewSelectEventArgs(e, null);

				if (e.NewSelectedIndex >= 0)
				{
					gvsea.NewSelectedEntity = this.DataSource.AsList[gvsea.NewSelectedIndex] as Entity;
				}

				SelectedIndexChanging.Invoke(this, gvsea);
			}
		}

		protected override void OnPageIndexChanging(GridViewPageEventArgs e)
		{
			this.PageIndex = e.NewPageIndex;
			
			this.EditIndex = -1;
			this.SelectedIndex = -1;

			KeepSorting();
		}

		protected override void OnRowEditing(GridViewEditEventArgs e)
		{
			base.OnRowEditing(e);
			this.EditIndex = e.NewEditIndex;
			this.SelectedIndex = e.NewEditIndex;
			KeepSorting();
		}

		protected override void OnRowDeleted(GridViewDeletedEventArgs e)
		{
		}

		protected override void OnRowDeleting(GridViewDeleteEventArgs e)
		{
			if (RowDeleting != null)
			{
				EntityGridViewDeleteEventArgs gvdea = new EntityGridViewDeleteEventArgs(e, null);

                int rowIndex = GetRealRowIndex(e.RowIndex);

				if (rowIndex >= 0)
				{
					gvdea.RowEntity = this.DataSource.AsList[rowIndex] as Entity;
				}
				RowDeleting.Invoke(this, gvdea);

				if(!e.Cancel)
				{
					gvdea.RowEntity.ActionState = EntityActionState.Deleted;
				}
			}
			this.EditIndex = -1;
			this.SelectedIndex = -1;
		}

		protected override void OnRowCancelingEdit(GridViewCancelEditEventArgs e)
		{
			base.OnRowCancelingEdit(e);
			this.EditIndex = -1;
			this.SelectedIndex = -1;
			KeepSorting();
		}

		protected override void OnRowUpdating(GridViewUpdateEventArgs e)
		{
			//base.OnRowUpdating(e);
			if (RowUpdating != null)
			{
				EntityGridViewUpdateEventArgs gvuea = new EntityGridViewUpdateEventArgs(e, null);

                int rowIndex = GetRealRowIndex(e.RowIndex);

				if (rowIndex >= 0)
				{
                    gvuea.RowEntity = this.DataSource.AsList[rowIndex] as Entity;

                    GridViewRow gvr = Rows[e.RowIndex];
					List<Control> controls = new List<Control>();

					foreach (TableCell cell in gvr.Cells)
					{
						AddAllControlsRecursively(cell, ref controls);
					}

					object oldValue = null;

					foreach (string key in Entity.Columns[gvuea.RowEntity.GetType()])
					{
						oldValue = gvuea.RowEntity.GetValue<object>(key);
                        gvuea.OldValues.Add(key, oldValue);
						gvuea.NewValues.Add(key, oldValue);
					}

					foreach(Control ctrl in controls)
					{
						object newValue = Page.Request.Form[ctrl.UniqueID];
                        if (newValue == null)
                        {
                            if (ctrl is CheckBox)
                            {
                                newValue = (ctrl as CheckBox).Checked;
                            }
                            if (ctrl is TextBox)
                            {
                                newValue = (ctrl as CheckBox).Text;
                            }
                        }
						if (newValue != null)
						{
                            if (ctrl is CheckBox && newValue.ToString().ToLower() == "on")
                            {
                                newValue = true;
                            }
                            DataControlFieldCell dcfc = ctrl.Parent as DataControlFieldCell;
							if (dcfc != null)
							{
								BoundField bf = dcfc.ContainingField as BoundField;
								if (bf != null)
								{
									gvuea.NewValues[bf.DataField] = newValue;
								}
							}
							else
							{
								int i = ctrl.ID.IndexOf('_');
								if(i >= 0)
								{
									string fieldName = ctrl.ID.Substring(i + 1);
									if (gvuea.NewValues.Contains(fieldName))
									{
										gvuea.NewValues[fieldName] = newValue;
									}
								}
							}
						}
					}

				}
				RowUpdating.Invoke(this, gvuea);

				if (!gvuea.Cancel)
				{
					bool isChanged = false;
					foreach (object key in gvuea.NewValues.Keys)
					{
						if (gvuea.NewValues[key] != gvuea.OldValues[key])
						{
                            string keyNew = key.ToString() + "New";
                            if (gvuea.RowEntity.Values.ContainsKey(keyNew))
                            {
                                gvuea.RowEntity.SetValue<object>(keyNew, gvuea.NewValues[key]);
                            }
                            gvuea.RowEntity.SetValue<object>(key.ToString(), gvuea.NewValues[key]);
                            isChanged = true;
						}
					}

					if (isChanged)
					{
						gvuea.RowEntity.ActionState = EntityActionState.Modified;
                        if (RowUpdated != null)
                        {
                            EntityGridViewUpdatedEventArgs gvudea = new EntityGridViewUpdatedEventArgs(rowIndex, gvuea.RowEntity);
                            foreach (object key in gvuea.OldValues.Keys)
                            {
                                gvudea.OldValues.Add(key, gvuea.OldValues[key]);
                            }
                            foreach (object key in gvuea.NewValues.Keys)
                            {
                                gvudea.NewValues.Add(key, gvuea.NewValues[key]);
                            }
                            RowUpdated.Invoke(this, gvudea);
                        }
					}
				}
			}

			this.EditIndex = -1;
			this.SelectedIndex = -1;

			KeepSorting();
		}

        protected override void OnRowUpdated(GridViewUpdatedEventArgs e)
        {
            base.OnRowUpdated(e);
        }

		protected override void OnRowCommand(GridViewCommandEventArgs e)
		{
			base.OnRowCommand(e);
			if (RowCommand != null)
			{
				EntityGridViewCommandEventArgs gvcea = new EntityGridViewCommandEventArgs(e, null);

				if (gvcea.SelectedIndex >= 0)
				{
					gvcea.SelectedEntity = this.DataSource.AsList[gvcea.SelectedIndex] as Entity;
				}
				RowCommand.Invoke(this, gvcea);
			}
		}

		protected override ICollection CreateColumns(PagedDataSource dataSource, bool useDataSource)
		{
			ICollection boundColumns = base.CreateColumns(dataSource, useDataSource);
			if (DataSource != null)
			{
				List<DataControlField> fieldList = new List<DataControlField>();
				List<DataControlField> all = new List<DataControlField>();

				foreach (DataControlField dcf in boundColumns)
				{
					string text = string.Empty;
					if (dcf is BoundField || dcf is TemplateField)
					{
						BoundField bf = dcf as BoundField;
						TemplateField tf = dcf as TemplateField;

						if (bf != null)
						{
							text = bf.DataField;
							fieldList.Add(bf);
						}
						if (tf != null)
						{
							text = tf.AccessibleHeaderText;
							fieldList.Add(tf);
						}

						if (string.IsNullOrEmpty(dcf.HeaderText))
						{
							dcf.HeaderText = text;
						}
						if (string.IsNullOrEmpty(dcf.SortExpression))
						{
							dcf.SortExpression = text;
						}
					}
					else
					{
						all.Add(dcf);
					}
				}

				fieldList.Sort(new DataControlOrderComparer(DataSource.ItemType));

				all.AddRange(fieldList);

				return all as ICollection;
			}
			else
			{
				return boundColumns;
			}
		}

		#endregion

		#region Overridden rendering

		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);
			if (this.HeaderRow != null)
			{
				int i = 0;
				int j = IndexOfColumn(LastExpression);
				foreach(TableCell cell in this.HeaderRow.Cells)
				{
					if (i++ == 0)
					{
						Literal lCaption = new Literal();
						lCaption.Text = mTitle;
						cell.CssClass = "tdGridHeaderTitle";
						cell.Controls.Clear();
						cell.Controls.Add(lCaption);
					}
					else
					{
						if (j == i)
						{
							Image img = new Image();
							img.ID = string.Format("{0}_img_{1}", this.ID, i);
							switch(LastDirection)
							{
								default:
								case SortDirection.Ascending:
									img.SkinID = "imgDown";
									break;
								case SortDirection.Descending:
									img.SkinID = "imgUp";
									break;
							}
							cell.Controls.AddAt(0, img);
							img.ApplyStyleSheetSkin(this.Page);
						}
					}
				}
			}
		}

		#endregion

		#region Private helper functions

		private int IndexOfColumn(string columnName)
		{
			if (string.IsNullOrEmpty(columnName))
			{
				return -1;
			}

			int j = 0;
			foreach (DataControlField dc in Columns)
			{
				if (dc.SortExpression == columnName)
				{
					return j + 2;
				}
				j++;
			}

			j = 0;
			foreach (TableCell n in HeaderRow.Cells)
			{
				LinkButton lb = new LinkButton();
				foreach (Control m in n.Controls)
				{
					lb = m as LinkButton;
					if (m != null)
					{
						break;
					}
				}

				if (lb != null && lb.Text == columnName)
				{
					return j + 1;
				}
				j++;
			}
			return -1;
		}

        private int GetRealRowIndex(int index)
        {
            return (this.PageIndex * this.PageSize) + index;
        }

		private void AddAllControlsRecursively(Control root, ref List<Control> controls)
		{
			controls.Add(root);

			foreach (Control ctrl in root.Controls)
			{
				AddAllControlsRecursively(ctrl, ref controls);
			}
		}

		#endregion

		#region Subclass for column ordering

		public class DataControlOrderComparer : IComparer<DataControlField>
		{
			private List<string> mList;

			public DataControlOrderComparer(Type entityType)
			{
				if (Entity.Columns.ContainsKey(entityType))
				{
					mList = Entity.Columns[entityType];
				}
				else
				{
					mList = new List<string>();
				}
			}

			#region IComparer<DataControlField> Members

			public int Compare(DataControlField x, DataControlField y)
			{
				BoundField bfX = x as BoundField;
				BoundField bfY = y as BoundField;

				if (bfX == null && bfY == null)
				{
					return 0;
				}

				if (bfX == null)
				{
					return -1;
				}

				if (bfY == null)
				{
					return -1;
				}

				return mList.IndexOf(bfX.DataField).CompareTo(mList.IndexOf(bfY.DataField));
			}

			#endregion
		}

		#endregion

	}
}